import { BufferGeometry, Vector3, Vector2, Quaternion, Matrix4, Curve, Float32BufferAttribute } from 'three';

class PathRecGeometry extends BufferGeometry {
  /**
   * @param {Editor.PathRecGeometryOptions}
   */
  constructor({ path, segments = 64, width = 1, height = 1, closed = false, threshold = 0.01 } = {}) {
    super();
    this.type = 'PathRecGeometry';

    // 存储参数
    this.path = path;
    this.segments = segments;
    this.width = width;
    this.height = height;
    this.closed = closed;
    this.threshold = threshold; // 检测拐点的阈值

    // 计算顶点、法线和UV
    this.compute();
  }

  compute() {
    const width = this.width;
    const height = this.height;
    const closed = this.closed;
    const threshold = this.threshold;

    // 自适应计算路径关键点
    const keyPointsInfo = this.getOptimizedKeyPoints(threshold);
    const points = keyPointsInfo.points;
    const tangents = keyPointsInfo.tangents;
    const segmentCounts = keyPointsInfo.segmentCounts;
    const totalSegments = keyPointsInfo.totalSegments;

    // 计算法线和副法线
    const normals = [];
    const binormals = [];

    // 计算第一个法线和副法线
    let normal = new Vector3();
    let binormal = new Vector3();
    const up = new Vector3(0, 1, 0);
    const vec = new Vector3();
    const tempQ = new Quaternion();

    // 计算第一个法线和副法线
    normal.copy(up).cross(tangents[0]).normalize();
    binormal.copy(tangents[0]).cross(normal).normalize();

    normals.push(normal.clone());
    binormals.push(binormal.clone());

    // 计算其余的法线和副法线
    for (let i = 1; i < points.length; i++) {
      normals.push(normals[i - 1].clone());
      binormals.push(binormals[i - 1].clone());

      // 如果不是闭合路径且是最后一个点，则跳过旋转计算
      if (!closed && i === points.length - 1) continue;

      // 计算旋转
      const t1 = tangents[i - 1];
      const t2 = tangents[i];

      // 计算两个切线之间的旋转
      vec.copy(t1).cross(t2);

      if (vec.length() > Number.EPSILON) {
        vec.normalize();
        const theta = Math.acos(Math.max(-1, Math.min(1, t1.dot(t2))));
        tempQ.setFromAxisAngle(vec, theta);

        // 应用旋转到法线和副法线
        normals[i].applyQuaternion(tempQ);
        binormals[i].applyQuaternion(tempQ);
      }

      // 防止法线和副法线共线
      binormals[i].copy(tangents[i]).cross(normals[i]).normalize();
      normals[i].copy(binormals[i]).cross(tangents[i]).normalize();
    }

    // 矩形横截面的顶点
    const rectangleVerts = [
      new Vector3(-width / 2, -height / 2, 0),
      new Vector3(width / 2, -height / 2, 0),
      new Vector3(width / 2, height / 2, 0),
      new Vector3(-width / 2, height / 2, 0),
    ];

    // 顶点、法线和UV数组
    const vertices = [];
    const normalsArray = [];
    const uvs = [];
    const indices = [];

    // 记录每个关键点在顶点数组中的起始索引
    const pointOffsets = [];
    let currentVertexIndex = 0;

    // 为每个关键点创建矩形横截面
    for (let i = 0; i < points.length; i++) {
      pointOffsets.push(currentVertexIndex);

      const position = points[i];
      const normal = normals[i];
      const binormal = binormals[i];

      // 为每个矩形顶点创建变换矩阵
      const matrix = new Matrix4();
      matrix.set(
        normal.x,
        binormal.x,
        tangents[i].x,
        position.x,
        normal.y,
        binormal.y,
        tangents[i].y,
        position.y,
        normal.z,
        binormal.z,
        tangents[i].z,
        position.z,
        0,
        0,
        0,
        1
      );

      for (let j = 0; j < 4; j++) {
        const vertex = rectangleVerts[j].clone().applyMatrix4(matrix);
        vertices.push(vertex.x, vertex.y, vertex.z);

        // 法线与矩形平面垂直
        const normalVector = new Vector3(0, 0, 1).applyQuaternion(new Quaternion().setFromRotationMatrix(matrix));
        normalsArray.push(normalVector.x, normalVector.y, normalVector.z);

        // UV坐标
        const u = i / (points.length - 1);
        let vCoord;
        if (j === 0) vCoord = 0;
        else if (j === 1) vCoord = 0.33;
        else if (j === 2) vCoord = 0.66;
        else vCoord = 1;
        uvs.push(u, vCoord);

        currentVertexIndex++;
      }
    }

    // 计算索引
    for (let i = 0; i < points.length - 1; i++) {
      const offset = pointOffsets[i];
      const nextOffset = pointOffsets[i + 1];

      // 每个矩形由两个三角形组成
      indices.push(offset, offset + 1, nextOffset + 1, offset, nextOffset + 1, nextOffset);
      indices.push(offset + 1, offset + 2, nextOffset + 2, offset + 1, nextOffset + 2, nextOffset + 1);
      indices.push(offset + 2, offset + 3, nextOffset + 3, offset + 2, nextOffset + 3, nextOffset + 2);
      indices.push(offset + 3, offset, nextOffset, offset + 3, nextOffset, nextOffset + 3);
    }

    // 处理闭合路径
    if (closed && points.length > 1) {
      const firstOffset = pointOffsets[0];
      const lastOffset = pointOffsets[pointOffsets.length - 1];

      indices.push(lastOffset, lastOffset + 1, firstOffset + 1, lastOffset, firstOffset + 1, firstOffset);
      indices.push(lastOffset + 1, lastOffset + 2, firstOffset + 2, lastOffset + 1, firstOffset + 2, firstOffset + 1);
      indices.push(lastOffset + 2, lastOffset + 3, firstOffset + 3, lastOffset + 2, firstOffset + 3, firstOffset + 2);
      indices.push(lastOffset + 3, lastOffset, firstOffset, lastOffset + 3, firstOffset, firstOffset + 3);
    }

    // 设置缓冲区数据
    this.setIndex(indices);
    this.setAttribute('position', new Float32BufferAttribute(vertices, 3));
    this.setAttribute('normal', new Float32BufferAttribute(normalsArray, 3));
    this.setAttribute('uv', new Float32BufferAttribute(uvs, 2));

    // 计算边界
    this.computeBoundingSphere();
    this.computeBoundingBox();
  }

  // 自适应计算路径关键点
  getOptimizedKeyPoints(threshold) {
    const segments = this.segments;
    const closed = this.closed;
    const path = this.path;

    // 初始均匀采样
    const initialPoints = [];
    const initialTangents = [];

    for (let i = 0; i <= segments; i++) {
      const t = i / segments;
      initialPoints.push(path.getPointAt(t));
      initialTangents.push(path.getTangentAt(t).normalize());
    }

    // 检测拐点并确定关键点
    const keyPoints = [initialPoints[0]];
    const keyTangents = [initialTangents[0]];
    const segmentCounts = []; // 每段的分段数

    let currentSegmentPoints = 1;

    for (let i = 1; i < initialPoints.length - 1; i++) {
      // 计算当前点的曲率（基于相邻切线的角度差）
      const tangent1 = initialTangents[i - 1];
      const tangent2 = initialTangents[i + 1];
      const dotProduct = Math.max(-1, Math.min(1, tangent1.dot(tangent2)));
      const angleDiff = Math.abs(Math.acos(dotProduct));

      // 如果角度差超过阈值，认为是拐点
      if (angleDiff > threshold) {
        keyPoints.push(initialPoints[i]);
        keyTangents.push(initialTangents[i]);
        segmentCounts.push(currentSegmentPoints);
        currentSegmentPoints = 1;
      } else {
        currentSegmentPoints++;
      }
    }

    // 添加最后一个点
    keyPoints.push(initialPoints[initialPoints.length - 1]);
    keyTangents.push(initialTangents[initialTangents.length - 1]);
    segmentCounts.push(currentSegmentPoints);

    // 计算总段数
    let totalSegments = 0;
    for (let count of segmentCounts) {
      totalSegments += count;
    }

    return {
      points: keyPoints,
      tangents: keyTangents,
      segmentCounts: segmentCounts,
      totalSegments: totalSegments,
    };
  }

  // 复制方法
  copy(source) {
    super.copy(source);
    this.path = source.path;
    this.segments = source.segments;
    this.width = source.width;
    this.height = source.height;
    this.closed = source.closed;
    this.threshold = source.threshold;
    return this;
  }

  // 克隆方法
  clone() {
    return new this.constructor(this.path, this.segments, this.width, this.height, this.closed, this.threshold).copy(this);
  }

  // 序列化方法
  toJSON() {
    const data = super.toJSON();
    data.path = this.path.uuid;
    data.segments = this.segments;
    data.width = this.width;
    data.height = this.height;
    data.closed = this.closed;
    data.threshold = this.threshold;
    return data;
  }
}

export { PathRecGeometry };
